package com.yonyou.pmclouds.basecom.async;

import com.yonyou.pmclouds.basecom.lock.PMLock;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 异步调用的过程的抽象类
 * <br/>继承时一般覆盖doXXXX方法，不要覆盖接口的实现方法
 */
public abstract class AbstractAsyncInvocation implements AsyncInvocation {
    protected AsyncInvocationInfo info;
    protected List<String> lockedKeys = new ArrayList<>();

    public AbstractAsyncInvocation(AsyncInvocationInfo info) {
        this.info = info;
    }

    /**
     * 被启动时，初始化上下文的逻辑
     */
    protected int doPrepare(InvokingTracer tracer) {
        info.setReady(true);
        info.setState(InvocationState.STATE_INVOKING);
        return info.getState();
    }

    /**
     * 被启动时，上下文在调用发起时已恢复，跳过的逻辑
     */
    protected int doSkipPrepare(InvokingTracer tracer) {
        return info.getState();
    }

    protected abstract int doInvoke(InvokingTracer tracer);

    protected abstract int doOnException(Exception ex, InvokingTracer tracer);

    protected void doClean(InvokingTracer tracer) {
    }

    protected boolean tryLock(String key, long lockTimeout) {
        try {
            lockedKeys.add(key);
            return PMLock.addLock(key, lockTimeout);

        } catch (InterruptedException e) {
            return false;
        }
    }

    /**
     * 加锁方法，将在clean的时候释放
     */
    protected boolean tryLock(String key) {
        return tryLock(key, PMLock.DEFAULT_TIME_OUT);
    }

    @Override
    public int prepare(InvokingTracer tracer) throws Exception {
        if (!tryLock(info.getInvocationId())) {
            return InvocationState.STATE_INVOKING_ORTHER;
        }
        // 判断异步任务是否因为等待时间过长，而被其他任务执行了
        AsyncInvocationInfo newestInfo = tracer.fetchNewestInfo(info.getInvocationId());
        if (newestInfo.getLastUpdateFlag() != null && !newestInfo.getLastUpdateFlag().equals(info.getLastUpdateFlag())) {
            return InvocationState.STATE_INVOKING_ORTHER;
        }

        RuntimeEnvironment.setTenantId(info.getTenantId());
        RuntimeEnvironment.setLoginUserId(info.getUserId());

        if (Boolean.TRUE.equals(info.getReady())) {
            info.setState(InvocationState.STATE_INVOKING);
            int state = doSkipPrepare(tracer);
            tracer.trace("prepare", info);
            return state;
        }
        int state = doPrepare(tracer);
        info.setReady(true);
        tracer.trace("prepare", info);
        return state;
    }

    @Override
    public int invoke(InvokingTracer tracer) throws Exception {
        int state = doInvoke(tracer);
        info.setState(state);
        info.setEndTime(new Date());
        tracer.trace("invoke", info);
        return state;
    }

    @Override
    public int onException(Exception ex, InvokingTracer tracer) {
        int state = doOnException(ex, tracer);
        info.setState(state);
        tracer.trace("exception", info);
        return state;
    }

    @Override
    public void cleanResource(InvokingTracer tracer) {
        // 先清理业务占用的资源，再去清理锁
        doClean(tracer);
        // 清理锁
        for (String lockedKey : lockedKeys) {
            try {
                PMLock.releaseLock(lockedKey);
            } catch (Exception ex) {
                // 如果一个锁释放出问题，也要继续释放
            }
        }
        RuntimeEnvironment.resetCurrentThread();
    }

    @Override
    public AsyncInvocationInfo getCurrentInfo() {
        return info;
    }
}
